Skip to content

Глава 5: Использование инструментов (Function Calling)

Обзор паттерна использования инструментов

До сих пор мы обсуждали агентные паттерны, которые в основном включают оркестрацию взаимодействий между языковыми моделями и управление потоком информации в рамках внутреннего рабочего процесса агента (цепочки промптов, маршрутизация, параллелизация, рефлексия). Однако для того чтобы агенты были по-настоящему полезными и могли взаимодействовать с реальным миром или внешними системами, им необходима способность использовать инструменты.

Паттерн использования инструментов, часто реализуемый через механизм, называемый Function Calling, позволяет агенту взаимодействовать с внешними API, базами данных, сервисами или даже выполнять код. Он позволяет LLM в основе агента решать, когда и как использовать конкретную внешнюю функцию на основе запроса пользователя или текущего состояния задачи.

Процесс обычно включает:

  1. Определение инструментов: Внешние функции или возможности определяются и описываются для LLM. Это описание включает назначение функции, её имя и параметры, которые она принимает, вместе с их типами и описаниями.
  2. Решение LLM: LLM получает запрос пользователя и доступные определения инструментов. На основе своего понимания запроса и инструментов, LLM решает, необходимо ли вызвать один или несколько инструментов для выполнения запроса.
  3. Генерация вызова функции: Если LLM решает использовать инструмент, он генерирует структурированный вывод (часто JSON-объект), который указывает имя инструмента для вызова и аргументы (параметры) для передачи ему, извлеченные из запроса пользователя.
  4. Выполнение инструмента: Агентный фреймворк или слой оркестрации перехватывает этот структурированный вывод. Он идентифицирует запрошенный инструмент и выполняет фактическую внешнюю функцию с предоставленными аргументами.
  5. Наблюдение/Результат: Вывод или результат выполнения инструмента возвращается агенту.
  6. Обработка LLM (опционально, но обычно): LLM получает вывод инструмента в качестве контекста и использует его для формулирования окончательного ответа пользователю или принятия решения о следующем шаге в рабочем процессе (который может включать вызов другого инструмента, рефлексию или предоставление окончательного ответа).

Этот паттерн является фундаментальным, поскольку он преодолевает ограничения обучающих данных LLM и позволяет ему получать доступ к актуальной информации, выполнять вычисления, которые он не может выполнить внутренне, взаимодействовать с пользовательскими данными или запускать действия в реальном мире. Function calling — это технический механизм, который соединяет возможности рассуждения LLM с огромным массивом доступных внешних функциональностей.

Хотя "вызов функций" точно описывает вызов конкретных, предопределенных функций кода, полезно рассмотреть более широкую концепцию "вызова инструментов". Этот более широкий термин признает, что возможности агента могут выходить далеко за рамки простого выполнения функций. "Инструмент" может быть традиционной функцией, но он также может быть сложной конечной точкой API, запросом к базе данных или даже инструкцией, направленной на другого специализированного агента. Эта перспектива позволяет нам представить более сложные системы, где, например, основной агент может делегировать сложную задачу анализа данных специализированному "агенту-аналитику" или запросить внешнюю базу знаний через её API. Мышление в терминах "вызова инструментов" лучше отражает полный потенциал агентов действовать как оркестраторы в разнообразной экосистеме цифровых ресурсов и других интеллектуальных сущностей.

Фреймворки, такие как LangChain, LangGraph и Google Agent Developer Kit (ADK), обеспечивают надежную поддержку для определения инструментов и интеграции их в рабочие процессы агентов, часто используя встроенные возможности вызова функций современных LLM, таких как в сериях Gemini или OpenAI. На "холсте" этих фреймворков вы определяете инструменты, а затем настраиваете агентов (обычно LLM-агентов) так, чтобы они знали о этих инструментах и могли их использовать.

Использование инструментов — это краеугольный паттерн для построения мощных, интерактивных и внешне осведомленных агентов.

Практические применения и случаи использования

Паттерн использования инструментов применим практически в любом сценарии, где агенту необходимо выйти за рамки генерации текста для выполнения действия или получения конкретной динамической информации:

  1. Получение информации из внешних источников:
    Доступ к данным в реальном времени или информации, которая отсутствует в обучающих данных LLM.
  • Случай использования: Агент прогноза погоды.
    • Инструмент: API погоды, которое принимает местоположение и возвращает текущие погодные условия.
    • Поток работы агента: Пользователь спрашивает: "Какая погода в Лондоне?", LLM определяет необходимость использования инструмента погоды, вызывает инструмент с "Лондон", инструмент возвращает данные, LLM форматирует данные в удобный для пользователя ответ.
  1. Взаимодействие с базами данных и API:
    Выполнение запросов, обновлений или других операций со структурированными данными.
  • Случай использования: Агент электронной коммерции.
    • Инструменты: API-вызовы для проверки инвентаря продуктов, получения статуса заказа или обработки платежей.
    • Поток работы агента: Пользователь спрашивает "Есть ли товар X на складе?", LLM вызывает API инвентаря, инструмент возвращает количество на складе, LLM сообщает пользователю статус наличия.
  1. Выполнение вычислений и анализа данных:
    Использование внешних калькуляторов, библиотек анализа данных или статистических инструментов.
  • Случай использования: Финансовый агент.
    • Инструменты: Функция калькулятора, API данных фондового рынка, инструмент для работы с электронными таблицами.
    • Поток работы агента: Пользователь спрашивает "Какая текущая цена AAPL и рассчитай потенциальную прибыль, если я купил 100 акций по $150?", LLM вызывает API акций, получает текущую цену, затем вызывает инструмент калькулятора, получает результат, форматирует ответ.
  1. Отправка коммуникаций:
    Отправка электронных писем, сообщений или выполнение API-вызовов к внешним коммуникационным сервисам.
  • Случай использования: Агент персонального помощника.
    • Инструмент: API отправки электронной почты.
    • Поток работы агента: Пользователь говорит: "Отправь письмо Джону о встрече завтра.", LLM вызывает инструмент электронной почты с получателем, темой и телом, извлеченными из запроса.
  1. Выполнение кода:
    Запуск фрагментов кода в безопасной среде для выполнения конкретных задач.
  • Случай использования: Агент помощника по программированию.
    • Инструмент: Интерпретатор кода.
    • Поток работы агента: Пользователь предоставляет фрагмент Python и спрашивает: "Что делает этот код?", LLM использует инструмент интерпретатора для запуска кода и анализа его вывода.
  1. Управление другими системами или устройствами:
    Взаимодействие с устройствами умного дома, IoT-платформами или другими подключенными системами.
  • Случай использования: Агент умного дома.
    • Инструмент: API для управления умными светильниками.
    • Поток работы агента: Пользователь говорит: "Выключи свет в гостиной." LLM вызывает инструмент умного дома с командой и целевым устройством.

Использование инструментов — это то, что превращает языковую модель из генератора текста в агента, способного воспринимать, рассуждать и действовать в цифровом или физическом мире (см. Рис. 1)

Рис.1: Некоторые примеры использования инструментов агентом

Практический пример кода (LangChain)

Реализация использования инструментов в фреймворке LangChain представляет собой двухэтапный процесс. Сначала определяются один или несколько инструментов, обычно путем инкапсуляции существующих Python-функций или других исполняемых компонентов. Затем эти инструменты привязываются к языковой модели, тем самым предоставляя модели возможность генерировать структурированный запрос на использование инструмента, когда она определяет, что для выполнения запроса пользователя требуется вызов внешней функции.

Следующая реализация продемонстрирует этот принцип, сначала определив простую функцию для имитации инструмента получения информации. После этого будет построен и настроен агент для использования этого инструмента в ответ на пользовательский ввод. Выполнение этого примера требует установки основных библиотек LangChain и пакета провайдера конкретной модели. Кроме того, необходимой предпосылкой является правильная аутентификация с выбранным сервисом языковой модели, обычно через API-ключ, настроенный в локальной среде.

python
import os
import getpass
import asyncio
import nest_asyncio
from typing import List
from dotenv import load_dotenv
import logging
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool as langchain_tool
from langchain.agents import create_tool_calling_agent, AgentExecutor

# UNCOMMENT
# Prompt the user securely and set API keys as an environment variables
os.environ["GOOGLE_API_KEY"] = getpass.getpass("Enter your Google API key: ")
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")

try:
    # A model with function/tool calling capabilities is required.
    llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0)
    print(f"✅ Language model initialized: {llm.model}")
except Exception as e:
    print(f"🛑 Error initializing language model: {e}")
    llm = None

# --- Define a Tool ---
@langchain_tool
def search_information(query: str) -> str:
    """
    Provides factual information on a given topic. Use this tool to find answers to phrases
    like 'capital of France' or 'weather in London?'.
    """
    print(f"\n--- 🛠️ Tool Called: search_information with query: '{query}' ---")
    # Simulate a search tool with a dictionary of predefined results.
    simulated_results = {
        "weather in london": "The weather in London is currently cloudy with a temperature of 15°C.",
        "capital of france": "The capital of France is Paris.",
        "population of earth": "The estimated population of Earth is around 8 billion people.",
        "tallest mountain": "Mount Everest is the tallest mountain above sea level.",
        "default": f"Simulated search result for '{query}': No specific information found, but the topic seems interesting."
    }
    result = simulated_results.get(query.lower(), simulated_results["default"])
    print(f"--- TOOL RESULT: {result} ---")
    return result

tools = [search_information]

# --- Create a Tool-Calling Agent ---
if llm:
    # This prompt template requires an `agent_scratchpad` placeholder for the agent's internal steps.
    agent_prompt = ChatPromptTemplate.from_messages([
        ("system", "You are a helpful assistant."),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ])

    # Create the agent, binding the LLM, tools, and prompt together.
    agent = create_tool_calling_agent(llm, tools, agent_prompt)

    # AgentExecutor is the runtime that invokes the agent and executes the chosen tools.
    # The 'tools' argument is not needed here as they are already bound to the agent.
    agent_executor = AgentExecutor(agent=agent, verbose=True, tools=tools)

async def run_agent_with_tool(query: str):
    """Invokes the agent executor with a query and prints the final response."""
    print(f"\n--- 🏃 Running Agent with Query: '{query}' ---")
    try:
        response = await agent_executor.ainvoke({"input": query})
        print("\n--- ✅ Final Agent Response ---")
        print(response["output"])
    except Exception as e:
        print(f"\n🛑 An error occurred during agent execution: {e}")

async def main():
    """Runs all agent queries concurrently."""
    tasks = [
        run_agent_with_tool("What is the capital of France?"),
        run_agent_with_tool("What's the weather like in London?"),
        run_agent_with_tool("Tell me something about dogs.") # Should trigger the default tool response
    ]
    await asyncio.gather(*tasks)

nest_asyncio.apply()
asyncio.run(main())

Код настраивает агента с вызовом инструментов, используя библиотеку LangChain и модель Google Gemini. Он определяет инструмент search_information, который имитирует предоставление фактических ответов на конкретные запросы. Инструмент имеет предопределенные ответы для "weather in london", "capital of france" и "population of earth", а также ответ по умолчанию для других запросов. Инициализируется модель ChatGoogleGenerativeAI, обеспечивающая возможности вызова инструментов. Создается ChatPromptTemplate для руководства взаимодействием агента. Функция create_tool_calling_agent используется для объединения языковой модели, инструментов и промпта в агента. Затем настраивается AgentExecutor для управления выполнением агента и вызовом инструментов. Определяется асинхронная функция run_agent_with_tool для вызова агента с заданным запросом и вывода результата. Основная асинхронная функция main подготавливает несколько запросов для одновременного выполнения. Эти запросы предназначены для тестирования как конкретных, так и стандартных ответов инструмента search_information. Наконец, вызов asyncio.run(main()) выполняет все задачи агента. Код включает проверки успешной инициализации LLM перед переходом к настройке и выполнению агента.

Практический пример кода (CrewAI)

Этот код предоставляет практический пример того, как реализовать вызов функций (инструментов) в фреймворке CrewAI. Он настраивает простой сценарий, где агент оснащен инструментом для поиска информации. Пример конкретно демонстрирует получение имитированной цены акций с использованием этого агента и инструмента.

python
# pip install crewai langchain-openai
import os
from crewai import Agent, Task, Crew
from crewai.tools import tool
import logging

# --- Best Practice: Configure Logging ---
# A basic logging setup helps in debugging and tracking the crew's execution.
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# --- Set up your API Key ---
# For production, it's recommended to use a more secure method for key management
# like environment variables loaded at runtime or a secret manager.
#
# Set the environment variable for your chosen LLM provider (e.g., OPENAI_API_KEY)
# os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"
# os.environ["OPENAI_MODEL_NAME"] = "gpt-4o"

# --- 1. Refactored Tool: Returns Clean Data ---
# The tool now returns raw data (a float) or raises a standard Python error.
# This makes it more reusable and forces the agent to handle outcomes properly.
@tool("Stock Price Lookup Tool")
def get_stock_price(ticker: str) -> float:
    """
    Fetches the latest simulated stock price for a given stock ticker symbol.
    Returns the price as a float. Raises a ValueError if the ticker is not found.
    """
    logging.info(f"Tool Call: get_stock_price for ticker '{ticker}'")
    simulated_prices = {
        "AAPL": 178.15,
        "GOOGL": 1750.30,
        "MSFT": 425.50,
    }
    price = simulated_prices.get(ticker.upper())
    if price is not None:
        return price
    else:
        # Raising a specific error is better than returning a string.
        # The agent is equipped to handle exceptions and can decide on the next action.
        raise ValueError(f"Simulated price for ticker '{ticker.upper()}' not found.")

# --- 2. Define the Agent ---
# The agent definition remains the same, but it will now leverage the improved tool.
financial_analyst_agent = Agent(
    role='Senior Financial Analyst',
    goal='Analyze stock data using provided tools and report key prices.',
    backstory="You are an experienced financial analyst adept at using data sources to find stock information. You provide clear, direct answers.",
    verbose=True,
    tools=[get_stock_price],
    # Allowing delegation can be useful, but is not necessary for this simple task.
    allow_delegation=False,
)

# --- 3. Refined Task: Clearer Instructions and Error Handling ---
# The task description is more specific and guides the agent on how to react
# to both successful data retrieval and potential errors.
analyze_aapl_task = Task(
    description=(
        "What is the current simulated stock price for Apple (ticker: AAPL)? "
        "Use the 'Stock Price Lookup Tool' to find it. "
        "If the ticker is not found, you must report that you were unable to retrieve the price."
    ),
    expected_output=(
        "A single, clear sentence stating the simulated stock price for AAPL. "
        "For example: 'The simulated stock price for AAPL is $178.15.' "
        "If the price cannot be found, state that clearly."
    ),
    agent=financial_analyst_agent,
)

# --- 4. Formulate the Crew ---
# The crew orchestrates how the agent and task work together.
financial_crew = Crew(
    agents=[financial_analyst_agent],
    tasks=[analyze_aapl_task],
    verbose=True  # Set to False for less detailed logs in production
)

# --- 5. Run the Crew within a Main Execution Block ---
# Using a __name__ == "__main__": block is a standard Python best practice.
def main():
    """Main function to run the crew."""
    # Check for API key before starting to avoid runtime errors.
    if not os.environ.get("OPENAI_API_KEY"):
        print("ERROR: The OPENAI_API_KEY environment variable is not set.")
        print("Please set it before running the script.")
        return

    print("\n## Starting the Financial Crew...")
    print("---------------------------------")

    # The kickoff method starts the execution.
    result = financial_crew.kickoff()
    print("\n---------------------------------")
    print("## Crew execution finished.")
    print("\nFinal Result:\n", result)

if __name__ == "__main__":
    main()

Этот код демонстрирует простое приложение, использующее библиотеку Crew.ai для имитации задачи финансового анализа. Он определяет пользовательский инструмент get_stock_price, который имитирует поиск цен акций для предопределенных тикеров. Инструмент предназначен для возврата числа с плавающей запятой для действительных тикеров или вызова ValueError для недействительных. Создается Crew.ai агент с именем financial_analyst_agent с ролью старшего финансового аналитика. Этому агенту предоставляется инструмент get_stock_price для взаимодействия. Определяется задача analyze_aapl_task, конкретно инструктирующая агента найти имитированную цену акций AAPL с использованием инструмента. Описание задачи включает четкие инструкции о том, как обрабатывать как случаи успеха, так и неудачи при использовании инструмента. Собирается команда, состоящая из financial_analyst_agent и analyze_aapl_task. Настройка verbose включена как для агента, так и для команды для обеспечения подробного логирования во время выполнения. Основная часть скрипта запускает задачу команды, используя метод kickoff() в стандартном блоке if name == "main":. Перед запуском команды он проверяет, установлена ли переменная окружения OPENAI_API_KEY, которая требуется для функционирования агента. Результат выполнения команды, который является выводом задачи, затем выводится на консоль. Код также включает базовую конфигурацию логирования для лучшего отслеживания действий команды и вызовов инструментов. Он использует переменные окружения для управления API-ключами, хотя отмечает, что более безопасные методы рекомендуются для производственных сред. Короче говоря, основная логика демонстрирует, как определять инструменты, агентов и задачи для создания совместного рабочего процесса в Crew.ai.

Практический пример кода (Google ADK)

Google Agent Developer Kit (ADK) включает библиотеку нативно интегрированных инструментов, которые могут быть напрямую включены в возможности агента.

Google Search: Основным примером такого компонента является инструмент Google Search. Этот инструмент служит прямым интерфейсом к поисковой системе Google, обеспечивая агента функциональностью для выполнения веб-поисков и получения внешней информации.

python
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.genai import types
import nest_asyncio
import asyncio

# Define variables required for Session setup and Agent execution
APP_NAME = "Google Search_agent"
USER_ID = "user1234"
SESSION_ID = "1234"

# Define Agent with access to search tool
root_agent = ADKAgent(
    name="basic_search_agent",
    model="gemini-2.0-flash-exp",
    description="Agent to answer questions using Google Search.",
    instruction="I can answer your questions by searching the internet. Just ask me anything!",
    tools=[google_search]  # Google Search is a pre-built tool to perform Google searches.
)

# Agent Interaction
async def call_agent(query):
    """
    Helper function to call the agent with a query.
    """
    # Session and Runner
    session_service = InMemorySessionService()
    session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
    runner = Runner(agent=root_agent, app_name=APP_NAME, session_service=session_service)

    content = types.Content(role='user', parts=[types.Part(text=query)])
    events = runner.run(user_id=USER_ID, session_id=SESSION_ID, new_message=content)

    for event in events:
        if event.is_final_response():
            final_response = event.content.parts[0].text
            print("Agent Response: ", final_response)

nest_asyncio.apply()
asyncio.run(call_agent("what's the latest ai news?"))

Этот код демонстрирует, как создать и использовать базового агента, работающего на Google ADK для Python. Агент предназначен для ответов на вопросы, используя Google Search в качестве инструмента. Сначала импортируются необходимые библиотеки из IPython, google.adk и google.genai. Определяются константы для имени приложения, идентификатора пользователя и идентификатора сессии. Создается экземпляр Agent с именем "basic_search_agent" с описанием и инструкциями, указывающими его назначение. Он настроен для использования инструмента Google Search, который является предварительно созданным инструментом, предоставляемым ADK. Инициализируется InMemorySessionService (см. Главу 8) для управления сессиями агента. Создается новая сессия для указанного приложения, пользователя и идентификаторов сессии. Создается экземпляр Runner, связывающий созданного агента с сервисом сессий. Этот runner отвечает за выполнение взаимодействий агента в рамках сессии. Определяется вспомогательная функция call_agent для упрощения процесса отправки запроса агенту и обработки ответа. Внутри call_agent запрос пользователя форматируется как объект types.Content с ролью 'user'. Вызывается метод runner.run с идентификатором пользователя, идентификатором сессии и содержимым нового сообщения. Метод runner.run возвращает список событий, представляющих действия и ответы агента. Код итерируется по этим событиям для поиска окончательного ответа. Если событие идентифицируется как окончательный ответ, извлекается текстовое содержимое этого ответа. Извлеченный ответ агента затем выводится на консоль. Наконец, функция call_agent вызывается с запросом "what's the latest ai news?" для демонстрации агента в действии.

Выполнение кода: Google ADK включает интегрированные компоненты для специализированных задач, включая среду для динамического выполнения кода. Инструмент built_in_code_execution предоставляет агенту изолированный интерпретатор Python. Это позволяет модели писать и запускать код для выполнения вычислительных задач, манипулирования структурами данных и выполнения процедурных скриптов. Такая функциональность критически важна для решения проблем, требующих детерминистической логики и точных вычислений, которые выходят за рамки только вероятностной генерации языка.

python
import os
import getpass
import asyncio
import nest_asyncio
from typing import List
from dotenv import load_dotenv
import logging
from google.adk.agents import Agent as ADKAgent, LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.adk.code_executors import BuiltInCodeExecutor
from google.genai import types

# Define variables required for Session setup and Agent execution
APP_NAME = "calculator"
USER_ID = "user1234"
SESSION_ID = "session_code_exec_async"

# Agent Definition
code_agent = LlmAgent(
    name="calculator_agent",
    model="gemini-2.0-flash",
    code_executor=BuiltInCodeExecutor(),
    instruction="""You are a calculator agent.
    When given a mathematical expression, write and execute Python code to calculate the result.
    Return only the final numerical result as plain text, without markdown or code blocks.
    """,
    description="Executes Python code to perform calculations.",
)

# Agent Interaction (Async)
async def call_agent_async(query):
    # Session and Runner
    session_service = InMemorySessionService()
    session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
    runner = Runner(agent=code_agent, app_name=APP_NAME, session_service=session_service)

    content = types.Content(role='user', parts=[types.Part(text=query)])
    print(f"\n--- Running Query: {query} ---")
    final_response_text = "No final text response captured."

    try:
        # Use run_async
        async for event in runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content):
            print(f"Event ID: {event.id}, Author: {event.author}")

            # --- Check for specific parts FIRST ---
            if event.content and event.content.parts and event.is_final_response():
                for part in event.content.parts:  # Iterate through all parts
                    if part.executable_code:
                        # Access the actual code string via .code
                        print(f"  Debug: Agent generated code:\n```python\n{part.executable_code.code}\n```")
                        has_specific_part = True
                    elif part.code_execution_result:
                        # Access outcome and output correctly
                        print(f"  Debug: Code Execution Result: {part.code_execution_result.outcome} - Output:\n{part.code_execution_result.output}")
                        has_specific_part = True
                    # Also print any text parts found in any event for debugging
                    elif part.text and not part.text.isspace():
                        print(f"  Text: '{part.text.strip()}'")

                # --- Check for final response AFTER specific parts ---
                text_parts = [part.text for part in event.content.parts if part.text]
                final_result = "".join(text_parts)
                print(f"==> Final Agent Response: {final_result}")
    except Exception as e:
        print(f"ERROR during agent run: {e}")

    print("-" * 30)

# Main async function to run the examples
async def main():
    await call_agent_async("Calculate the value of (5 + 7) * 3")
    await call_agent_async("What is 10 factorial?")

# Execute the main async function
try:
    nest_asyncio.apply()
    asyncio.run(main())
except RuntimeError as e:
    # Handle specific error when running asyncio.run in an already running loop (like Jupyter/Colab)
    if "cannot be called from a running event loop" in str(e):
        print("\nRunning in an existing event loop (like Colab/Jupyter).")
        print("Please run `await main()` in a notebook cell instead.")
        # If in an interactive environment like a notebook, you might need to run:
        # await main()
    else:
        raise e  # Re-raise other runtime errors

Этот скрипт использует Google Agent Development Kit (ADK) для создания агента, который решает математические проблемы, написав и выполнив Python-код. Он определяет LlmAgent, специально инструктированного действовать как калькулятор, оснащая его инструментом built_in_code_execution. Основная логика находится в функции call_agent_async, которая отправляет запрос пользователя runner'у агента и обрабатывает полученные события. Внутри этой функции асинхронный цикл итерируется по событиям, выводя сгенерированный Python-код и результат его выполнения для отладки. Код тщательно различает между этими промежуточными шагами и финальным событием, содержащим числовой ответ. Наконец, основная функция запускает агента с двумя различными математическими выражениями для демонстрации его способности выполнять вычисления.

Корпоративный поиск: Этот код определяет приложение Google ADK, использующее библиотеку google.adk в Python. Он специально использует VSearchAgent, который предназначен для ответов на вопросы путем поиска в указанном хранилище данных Vertex AI Search. Код инициализирует VSearchAgent с именем "q2_strategy_vsearch_agent", предоставляя описание, модель для использования ("gemini-2.0-flash-exp") и ID хранилища данных Vertex AI Search. DATASTORE_ID ожидается быть установленным как переменная окружения. Затем он настраивает Runner для агента, используя InMemorySessionService для управления историей разговоров. Определяется асинхронная функция call_vsearch_agent_async для взаимодействия с агентом. Эта функция принимает запрос, создает объект содержимого сообщения и вызывает метод run_async runner'а для отправки запроса агенту. Функция затем потоково передает ответ агента обратно на консоль по мере его поступления. Она также выводит информацию об окончательном ответе, включая любые атрибуции источников из хранилища данных. Включена обработка ошибок для перехвата исключений во время выполнения агента, предоставляя информативные сообщения о потенциальных проблемах, таких как неправильный ID хранилища данных или отсутствующие разрешения. Предоставляется еще одна асинхронная функция run_vsearch_example для демонстрации того, как вызывать агента с примерными запросами. Основной блок выполнения проверяет, установлен ли DATASTORE_ID, а затем запускает пример, используя asyncio.run. Он включает проверку для обработки случаев, когда код запускается в среде, которая уже имеет работающий цикл событий, как блокнот Jupyter.

python
import asyncio
from google.genai import types
from google.adk import agents
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
import os

# --- Configuration ---
# Ensure you have set your GOOGLE_API_KEY and DATASTORE_ID environment variables
# For example:
# os.environ["GOOGLE_API_KEY"] = "YOUR_API_KEY"
# os.environ["DATASTORE_ID"] = "YOUR_DATASTORE_ID"
DATASTORE_ID = os.environ.get("DATASTORE_ID")

# --- Application Constants ---
APP_NAME = "vsearch_app"
USER_ID = "user_123"  # Example User ID
SESSION_ID = "session_456"  # Example Session ID

# --- Agent Definition (Updated with the newer model from the guide) ---
vsearch_agent = agents.VSearchAgent(
    name="q2_strategy_vsearch_agent",
    description="Answers questions about Q2 strategy documents using Vertex AI Search.",
    model="gemini-2.0-flash-exp",  # Updated model based on the guide's examples
    datastore_id=DATASTORE_ID,
    model_parameters={"temperature": 0.0}
)

# --- Runner and Session Initialization ---
runner = Runner(
    agent=vsearch_agent,
    app_name=APP_NAME,
    session_service=InMemorySessionService(),
)

# --- Agent Invocation Logic ---
async def call_vsearch_agent_async(query: str):
    """Initializes a session and streams the agent's response."""
    print(f"User: {query}")
    print("Agent: ", end="", flush=True)

    try:
        # Construct the message content correctly
        content = types.Content(role='user', parts=[types.Part(text=query)])

        # Process events as they arrive from the asynchronous runner
        async for event in runner.run_async(
            user_id=USER_ID,
            session_id=SESSION_ID,
            new_message=content
        ):
            # For token-by-token streaming of the response text
            if hasattr(event, 'content_part_delta') and event.content_part_delta:
                print(event.content_part_delta.text, end="", flush=True)

            # Process the final response and its associated metadata
            if event.is_final_response():
                print()  # Newline after the streaming response
                if event.grounding_metadata:
                    print(f"  (Source Attributions: {len(event.grounding_metadata.grounding_attributions)} sources found)")
                else:
                    print("  (No grounding metadata found)")
                print("-" * 30)
    except Exception as e:
        print(f"\nAn error occurred: {e}")
        print("Please ensure your datastore ID is correct and that the service account has the necessary permissions.")
        print("-" * 30)

# --- Run Example ---
async def run_vsearch_example():
    # Replace with a question relevant to YOUR datastore content
    await call_vsearch_agent_async("Summarize the main points about the Q2 strategy document.")
    await call_vsearch_agent_async("What safety procedures are mentioned for lab X?")

# --- Execution ---
if __name__ == "__main__":
    if not DATASTORE_ID:
        print("Error: DATASTORE_ID environment variable is not set.")
    else:
        try:
            asyncio.run(run_vsearch_example())
        except RuntimeError as e:
            # This handles cases where asyncio.run is called in an environment
            # that already has a running event loop (like a Jupyter notebook).
            if "cannot be called from a running event loop" in str(e):
                print("Skipping execution in a running event loop. Please run this script directly.")
            else:
                raise e

В целом, этот код предоставляет базовую структуру для построения разговорного AI-приложения, которое использует Vertex AI Search для ответов на вопросы на основе информации, хранящейся в хранилище данных. Он демонстрирует, как определить агента, настроить runner и взаимодействовать с агентом асинхронно при потоковой передаче ответа. Фокус заключается в получении и синтезе информации из конкретного хранилища данных для ответов на запросы пользователей.

Vertex Extensions: Расширение Vertex AI — это структурированная обертка API, которая позволяет модели подключаться к внешним API для обработки данных в реальном времени и выполнения действий. Расширения предлагают безопасность корпоративного уровня, конфиденциальность данных и гарантии производительности. Они могут использоваться для задач, таких как генерация и выполнение кода, запросы к веб-сайтам и анализ информации из частных хранилищ данных. Google предоставляет предварительно созданные расширения для общих случаев использования, таких как Code Interpreter и Vertex AI Search, с возможностью создания пользовательских. Основное преимущество расширений включает строгие корпоративные элементы управления и бесшовную интеграцию с другими продуктами Google. Ключевое различие между расширениями и вызовом функций заключается в их выполнении: Vertex AI автоматически выполняет расширения, в то время как вызовы функций требуют ручного выполнения пользователем или клиентом.

Краткий обзор

Что: LLM являются мощными генераторами текста, но они принципиально отключены от внешнего мира. Их знания статичны, ограничены данными, на которых они были обучены, и они лишены способности выполнять действия или получать информацию в реальном времени. Это врожденное ограничение препятствует им в выполнении задач, требующих взаимодействия с внешними API, базами данных или сервисами. Без моста к этим внешним системам их полезность для решения реальных проблем серьезно ограничена.

Почему: Паттерн использования инструментов, часто реализуемый через вызов функций, предоставляет стандартизированное решение этой проблемы. Он работает, описывая доступные внешние функции или "инструменты" для LLM способом, который он может понять. Основываясь на запросе пользователя, агентная LLM может затем решить, нужен ли инструмент, и сгенерировать структурированный объект данных (например, JSON), указывающий, какую функцию вызвать и с какими аргументами. Слой оркестрации выполняет этот вызов функции, получает результат и передает его обратно LLM. Это позволяет LLM включить актуальную внешнюю информацию или результат действия в свой окончательный ответ, эффективно предоставляя ему способность действовать.

Эмпирическое правило: Используйте паттерн использования инструментов всякий раз, когда агенту необходимо выйти за рамки внутренних знаний LLM и взаимодействовать с внешним миром. Это необходимо для задач, требующих данных в реальном времени (например, проверка погоды, цен на акции), доступа к частной или проприетарной информации (например, запросы к базе данных компании), выполнения точных вычислений, выполнения кода или запуска действий в других системах (например, отправка электронной почты, управление умными устройствами).

Визуальное резюме:

Рис.2: Паттерн проектирования использования инструментов

Ключевые выводы

  • Использование инструментов (Function Calling) позволяет агентам взаимодействовать с внешними системами и получать доступ к динамической информации.
  • Это включает определение инструментов с четкими описаниями и параметрами, которые LLM может понять.
  • LLM решает, когда использовать инструмент, и генерирует структурированные вызовы функций.
  • Агентные фреймворки выполняют фактические вызовы инструментов и возвращают результаты LLM.
  • Использование инструментов необходимо для создания агентов, которые могут выполнять действия в реальном мире и предоставлять актуальную информацию.
  • LangChain упрощает определение инструментов, используя декоратор @tool, и предоставляет create_tool_calling_agent и AgentExecutor для создания агентов, использующих инструменты.
  • Google ADK имеет ряд очень полезных предварительно созданных инструментов, таких как Google Search, Code Execution и Vertex AI Search Tool.

Заключение

Паттерн использования инструментов является критическим архитектурным принципом для расширения функциональной области больших языковых моделей за пределы их внутренних возможностей генерации текста. Оснащая модель способностью взаимодействовать с внешним программным обеспечением и источниками данных, эта парадигма позволяет агенту выполнять действия, выполнять вычисления и получать информацию из других систем. Этот процесс включает генерацию моделью структурированного запроса на вызов внешнего инструмента, когда она определяет, что это необходимо для выполнения запроса пользователя. Фреймворки, такие как LangChain, Google ADK и Crew AI, предлагают структурированные абстракции и компоненты, которые облегчают интеграцию этих внешних инструментов. Эти фреймворки управляют процессом предоставления спецификаций инструментов модели и разбора её последующих запросов на использование инструментов. Это упрощает разработку сложных агентных систем, которые могут взаимодействовать и предпринимать действия в внешних цифровых средах.

Ссылки

  1. LangChain Documentation (Tools)
  2. Google Agent Developer Kit (ADK) Documentation (Tools)
  3. OpenAI Function Calling Documentation
  4. CrewAI Documentation (Tools)

Навигация

Назад: Глава 4. Рефлексия
Вперед: Глава 6. Планирование